﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace Toenda.Foundation.Utility {
	/// <summary>
	/// Implements a factory class to create signatures from different types
	/// </summary>
	public class SignatureFactory {
		#region API
		
		[StructLayoutAttribute(LayoutKind.Sequential)]
		private struct GUIDDATA {
			public int Data1;
			public short Data2;
			public short Data3;

			[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 8)]
			public byte[] Data4;
		}

		[DllImportAttribute("rpcrt4.dll")]
		private static extern int UuidCreateSequential(out GUIDDATA Uuid);
		#endregion

		public static Signature CreateId(SignatureType type) {
			switch(type) {
				case SignatureType.TickId:
					return new Signature(NewTickId(), SignatureType.TickId);

				case SignatureType.SequentialGuid:
					return new Signature(NewSequentialId().ToString(), SignatureType.SequentialGuid);

				case SignatureType.Guid:
					return new Signature(NewId().ToString(), SignatureType.Guid);

				case SignatureType.TimestampId:
					return new Signature(NewTimestampId(), SignatureType.TimestampId);

				default:
					return null;
			}
		}

		#region Private Members

		private static long latestTick;
		private static int latestTickOccurences = 0;
		private static object staticLocker = new object();

		/// <summary>
		/// Creates a GUID
		/// </summary>
		/// <returns>Guid</returns>
		private static Guid NewId() {
			return Guid.NewGuid();
		}

		/// <summary>
		/// Generates the biggest Guid
		/// </summary>
		/// <returns>GUID</returns>
		private static Guid NewSequentialId() {
			GUIDDATA guiddata;

			if((UuidCreateSequential(out guiddata) & 0x80000000) != 0) {
				// FAILED(hr)  
				throw new InvalidOperationException();
			}

			return new Guid(
				guiddata.Data1,
				guiddata.Data2,
				guiddata.Data3,
				guiddata.Data4
			);
		}

		/// <summary>
		/// Create a tick id
		/// </summary>
		/// <returns></returns>
		private static string NewTickId() {
			StringBuilder str = new StringBuilder();

			lock(staticLocker) {
				long tick = DateTime.Now.Ticks;

				if(tick == latestTick) {
					latestTickOccurences++;
				}
				else {
					latestTick = tick;
					latestTickOccurences = 0;
				}

				str.Append(tick.ToString());
				str.Append("_");
				str.Append(latestTickOccurences.ToString());
				str.Append("_");
				str.Append(NewId());
			}

			return str.ToString();
		}

		/// <summary>
		/// Creates a timestamp id
		/// </summary>
		/// <returns></returns>
		private static string NewTimestampId() {
			StringBuilder str = new StringBuilder();

			DateTime dt = DateTime.Now;

			str.Append(dt.Year.ToString());
			str.Append(dt.Month.ToString());
			str.Append(dt.Day.ToString());
			str.Append("_");
			str.Append(dt.Hour.ToString());
			str.Append(dt.Minute.ToString());
			str.Append(dt.Second.ToString());
			str.Append("_");
			str.Append(dt.Millisecond.ToString());
			str.Append("_");
			str.Append(dt.Ticks.ToString());
			str.Append("_");
			str.Append(NewId());

			return str.ToString();
		}

		#endregion
	}
}
